package ar.com.temporis.framework.persistence;

import org.hibernate.ObjectNotFoundException;

import ar.com.temporis.framework.AbstractERPJTestCase;
import ar.com.temporis.framework.common.comparator.BeanComparator;
import ar.com.temporis.framework.domain.Entity;



/**
 * 
 * @author matias.sulik
 * 
 */
public abstract class PersistenceTestCase extends AbstractERPJTestCase {


    /**
     * DAO que sirve como helper para realizar las operaciones de persistencia
     * genericas
     */
    private PersistenceTestDAO persistenceTestDAO;

    private void compareObjects(Entity current, Entity persisted) {
      BeanComparator comparator = (BeanComparator) applicationContext.getBean("beanComparator");
      comparator.compare(current, persisted);
      // TODO: fix the comparison, it is not working with the Spring generated proxies
      // assertTrue(comparator.comparisonResult());
    }

    /**
     * Este metodo deberia ser definido por las subclases y deberian definir la
     * forma en la que se crea la entidad que se intenta testear
     * 
     * @return una instancia de la entidad a testear
     */
    protected abstract Entity createEntity();

    private Entity getPersistedEntity(Entity entity) {
        return (Entity) persistenceTestDAO.read(entity.getId(), entity.getClass());
    }

    /**
     * @return determina si la entidad a testear es mutable o no. (read-only).
     */
    protected boolean isInmutable() {
        return false;
    }

    /**
     * Este metodo deberia ser definido por las subclases y deberian definir la
     * forma en la que se modifica la entidad que se intenta testear
     * 
     */
    protected abstract void modify(Entity entity);

    @Override
    protected void onSetUp() throws Exception {
        super.onSetUp();

        persistenceTestDAO = (PersistenceTestDAO) applicationContext.getBean("persistenceTestDAO");

        persistenceSetUp();
    }

    private void performDelete(Entity entity) {
        if (isInmutable())
            return;

        // resetSession();
        persistenceTestDAO.delete(entity);

        try {
            resetSession();
            getPersistedEntity(entity);
        } catch (ObjectNotFoundException e) {
        }
    }

    private void performSave(Entity entity) {
        persistenceTestDAO.persist(entity);

        resetSession();
        Entity persisted = getPersistedEntity(entity);
        assertTrue(persisted != entity);
    }

    private void performUpdate(Entity entity) {

        if (isInmutable())
            return;

        resetSession();
        modify(entity);
        persistenceTestDAO.update(entity);

        Entity persisted = getPersistedEntity(entity);
        this.compareObjects(entity, persisted);
    }

    /**
     * Este metodo reemplaza al {@link #onSetUp()} y define las operaciones que
     * se realizan antes de comenzar el test
     */
    protected void persistenceSetUp() {
    }

    private void resetSession() {
        persistenceTestDAO.flush();
        persistenceTestDAO.clear();
    }

    /**
     * Este metodo es el que se ejecutara por junit y es el que define el
     * template de ejecucion del test de persistencia.
     */
    public void test() {
        Entity entity = createEntity();
        performSave(entity);
        performUpdate(entity);
        performDelete(entity);
    }
    
    protected final < T1 > T1 getAny(Class<T1> clazz) {
    	return this.persistenceTestDAO.getAny(clazz);
    }

}
